import Panel, { PanelType } from "./Panel";
import PanelAnimation from "./PanelAnimation";

/**
 * 面板层级
 */
export enum PanelLayer {
    TOP,
    MIDDLE,
    BOTTOM
}

/**
 * 面板参数
 */
export interface PanelOption {
    panelData?: any,
    panelAnimationData?: any,
}

/**
 * ui管理：
 * 面板动画
 * 面板缓存
 */

export default class UiManager extends cc.EventTarget {
    public static EventType = {
        OPEN_ANIMATION_END: "open-animation-end",
        CLOSE_ANIMATION_END: "close-animation-end",
        PANEL_OPEN: "panel-open",
        PANEL_CLOSE: "panel-close"
    }

    private static _instance: UiManager = null;
    public static getInstance(): UiManager {
        return this._instance || (this._instance = new UiManager());
    }

    private _topLayer: cc.Node = null;
    private _middleLayer: cc.Node = null;
    private _bottomLayer: cc.Node = null;
    private _panelMap: Map<string, Panel> = null;
    private _loadingPanel: Map<string, boolean> = null;
    private _cachePanel: Map<string, Panel> = null;

    constructor() {
        super();
        this._panelMap = new Map();
        this._loadingPanel = new Map();
        this._cachePanel = new Map();

        this.on(UiManager.EventType.OPEN_ANIMATION_END, this.onOpenAnimationEnd, this);
        this.on(UiManager.EventType.CLOSE_ANIMATION_END, this.onCloseAnimationEnd, this);
    }

    /**
     * 打开面板
     * @param path 
     * @param option 
     * @param layer 
     */
    open(path: string, option: PanelOption = {}, layer: PanelLayer = PanelLayer.MIDDLE) {
        this.checkPersistNode();
        if (this.hasPanel(path)) {
            return cc.log("panel: " + path + " already exist");
        } else if (this._cachePanel.get(path)) {
            //使用缓存面板
            this._onPanelLoaded(this._cachePanel.get(path).node, path, option, layer);
        } else {
            this._loadingPanel.set(path, true);
            cc.resources.load(path, (error, asset) => {
                if (error) {
                    cc.warn(error);
                } else {
                    this.onPanelLoaded(asset as cc.Prefab, path, option, layer);
                }
            });
        }
    }

    private onPanelLoaded(asset: cc.Prefab, path: string, option: PanelOption, layer: PanelLayer) {
        if (this._loadingPanel.get(path)) {
            this._loadingPanel.delete(path);
            let panelNode = cc.instantiate(asset);
            this._onPanelLoaded(panelNode, path, option, layer);
            //资源引用++
            asset.addRef();
        }
    }

    private _onPanelLoaded(panelNode: cc.Node, path: string, option: PanelOption, layer: PanelLayer) {
        let panel = panelNode.getComponent(Panel);
        if (!panel) return cc.warn("panel component is not exist");
        panel.data = option.panelData;
        panel.path = path;
        this._panelMap.set(path, panel);
        //面板动画
        let panelAnimation = panelNode.getComponent(PanelAnimation);
        panelAnimation && (panelAnimation.data = option.panelAnimationData);
        panel.node.parent = this.getLayer(layer);

        if (panel.type === PanelType.FULL_SCREEN) {
            panel.node.setPosition(cc.Vec2.ZERO_R);
            panel.node.setContentSize(cc.winSize);
        }

        panel.beforeOpen();
        if (panelAnimation) {
            panelAnimation.playOpenAnimaiton();
        } else {
            this.onOpenAnimationEnd(panel);
        }
    }

    private onOpenAnimationEnd(panel: Panel) {
        panel.afterOpen();
        this.emit(UiManager.EventType.PANEL_OPEN, panel.path);
    }

    private onCloseAnimationEnd(panel: Panel) {
        panel.afterClose();
        this._close(panel);
    }

    /**
     * 关闭面板
     * @param pathOrPanel 
     * @returns 
     */
    public close(pathOrPanel: string | Panel) {
        if (!(pathOrPanel instanceof Panel)) {
            if (this._loadingPanel.get(pathOrPanel)) {
                return this._loadingPanel.delete(pathOrPanel);
            } else if (this.hasPanel(pathOrPanel)) {
                pathOrPanel = this.getPanel(pathOrPanel);
            } else {
                return;
            }
        }

        pathOrPanel.beforeClose();
        let panelAnimation = pathOrPanel.node.getComponent(PanelAnimation);
        if (panelAnimation) {
            panelAnimation.playCloseAnimation();
        } else {
            pathOrPanel.afterClose();
            this._close(pathOrPanel);
        }
    }

    private _close(panel: Panel) {
        if (panel.cache) {
            panel.node.removeFromParent();
            this._panelMap.delete(panel.path);
            this._cachePanel.set(panel.path, panel);
        } else {
            panel.node.destroy();
            this._panelMap.delete(panel.path);
            this._cachePanel.delete(panel.path);
            let asset = cc.resources.get(panel.path);
            //资源引用--
            asset.decRef();
        }

        this.emit(UiManager.EventType.PANEL_CLOSE, panel.path);
    }

    public getPanel(path: string): Panel {
        return this._panelMap.get(path);
    }

    public hasPanel(path: string): boolean {
        return !!this._panelMap.get(path);
    }

    private checkPersistNode() {
        if (!this._topLayer) {
            let node = new cc.Node();
            node.zIndex = 0;
            node.name = 'top-layer';
            cc.game.addPersistRootNode(node);
            node.setPosition(cc.v2(cc.winSize.width * 0.5, cc.winSize.height * 0.5));
            this._topLayer = node;
        }
        if (!this._middleLayer) {
            let node = new cc.Node();
            node.zIndex = 1;
            node.name = 'middle-layer';
            cc.game.addPersistRootNode(node);
            node.setPosition(cc.v2(cc.winSize.width * 0.5, cc.winSize.height * 0.5));
            this._middleLayer = node;
        }
        if (!this._bottomLayer) {
            let node = new cc.Node();
            node.zIndex = 2;
            node.name = 'bottom-layer';
            cc.game.addPersistRootNode(node);
            node.setPosition(cc.v2(cc.winSize.width * 0.5, cc.winSize.height * 0.5));
            this._bottomLayer = node;
        }
    }


    private getLayer(layer: PanelLayer) {
        let node;
        switch (layer) {
            case PanelLayer.TOP:
                node = this._topLayer;
                break;
            case PanelLayer.MIDDLE:
                node = this._middleLayer;
                break;
            case PanelLayer.BOTTOM:
                node = this._bottomLayer;
                break;
        }
        return node;
    }
}

function DialogAnimation(DialogAnimation: any) {
    throw new Error("Function not implemented.");
}
